{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from tensorflow.keras.applications.vgg16 import VGG16,preprocess_input\n",
    "from tensorflow.keras.models import Sequential\n",
    "from tensorflow.keras.preprocessing import image\n",
    "from tensorflow.keras.layers import Activation,Dropout,Flatten,Dense\n",
    "from tensorflow.python.keras.utils import np_utils\n",
    "from tensorflow.keras.optimizers import Adam\n",
    "from tensorflow.keras.preprocessing.image import ImageDataGenerator\n",
    "import os\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/vgg16/vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5\n",
      "58892288/58889256 [==============================] - 27s 0us/step\n"
     ]
    }
   ],
   "source": [
    "#载入预训练的VGG16模型，不包括全连接层\n",
    "model = VGG16(weights='imagenet',include_top=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model: \"vgg16\"\n",
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "input_1 (InputLayer)         [(None, None, None, 3)]   0         \n",
      "_________________________________________________________________\n",
      "block1_conv1 (Conv2D)        (None, None, None, 64)    1792      \n",
      "_________________________________________________________________\n",
      "block1_conv2 (Conv2D)        (None, None, None, 64)    36928     \n",
      "_________________________________________________________________\n",
      "block1_pool (MaxPooling2D)   (None, None, None, 64)    0         \n",
      "_________________________________________________________________\n",
      "block2_conv1 (Conv2D)        (None, None, None, 128)   73856     \n",
      "_________________________________________________________________\n",
      "block2_conv2 (Conv2D)        (None, None, None, 128)   147584    \n",
      "_________________________________________________________________\n",
      "block2_pool (MaxPooling2D)   (None, None, None, 128)   0         \n",
      "_________________________________________________________________\n",
      "block3_conv1 (Conv2D)        (None, None, None, 256)   295168    \n",
      "_________________________________________________________________\n",
      "block3_conv2 (Conv2D)        (None, None, None, 256)   590080    \n",
      "_________________________________________________________________\n",
      "block3_conv3 (Conv2D)        (None, None, None, 256)   590080    \n",
      "_________________________________________________________________\n",
      "block3_pool (MaxPooling2D)   (None, None, None, 256)   0         \n",
      "_________________________________________________________________\n",
      "block4_conv1 (Conv2D)        (None, None, None, 512)   1180160   \n",
      "_________________________________________________________________\n",
      "block4_conv2 (Conv2D)        (None, None, None, 512)   2359808   \n",
      "_________________________________________________________________\n",
      "block4_conv3 (Conv2D)        (None, None, None, 512)   2359808   \n",
      "_________________________________________________________________\n",
      "block4_pool (MaxPooling2D)   (None, None, None, 512)   0         \n",
      "_________________________________________________________________\n",
      "block5_conv1 (Conv2D)        (None, None, None, 512)   2359808   \n",
      "_________________________________________________________________\n",
      "block5_conv2 (Conv2D)        (None, None, None, 512)   2359808   \n",
      "_________________________________________________________________\n",
      "block5_conv3 (Conv2D)        (None, None, None, 512)   2359808   \n",
      "_________________________________________________________________\n",
      "block5_pool (MaxPooling2D)   (None, None, None, 512)   0         \n",
      "=================================================================\n",
      "Total params: 14,714,688\n",
      "Trainable params: 14,714,688\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n"
     ]
    }
   ],
   "source": [
    "model.summary()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "datagen = ImageDataGenerator(\n",
    "             rotation_range = 40,      #随机翻转角度\n",
    "             width_shift_range = 0.2,  #随机水平平移\n",
    "             height_shift_range=0.2,   #随机竖直平移\n",
    "             rescale=1./255,           #数值归一化\n",
    "             shear_range=0.2,          #随机裁剪\n",
    "             zoom_range=0.2,           #随机放大\n",
    "             horizontal_flip=True,     #水平翻转\n",
    "             fill_mode='nearest')      #填充方式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Found 25000 images belonging to 2 classes.\n",
      "WARNING:tensorflow:From <ipython-input-6-8e54c80996ee>:13: Model.predict_generator (from tensorflow.python.keras.engine.training) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Please use Model.predict, which supports generators.\n"
     ]
    }
   ],
   "source": [
    "batch_size = 32\n",
    "# \n",
    "train_steps = int((2000 +  batch_size - 1)/batch_size)*10\n",
    "test_steps = int((1000 +  batch_size - 1)/batch_size)*10\n",
    "generator = datagen.flow_from_directory(\n",
    "        'Images/train',\n",
    "        target_size=(150, 150),\n",
    "        batch_size=batch_size,\n",
    "        class_mode=None,  # 不生成标签\n",
    "        shuffle=False)    # 不随机打乱\n",
    "\n",
    "# 得到训练集数据\n",
    "bottleneck_features_train = model.predict_generator(generator, train_steps)\n",
    "print(bottleneck_features_train.shape)\n",
    "# 保存训练集bottleneck结果\n",
    "np.save(open('bottleneck_features_train.npy', 'wb'), bottleneck_features_train)\n",
    "\n",
    "generator = datagen.flow_from_directory(\n",
    "        'Images/test',\n",
    "        target_size=(150, 150),\n",
    "        batch_size=batch_size,\n",
    "        class_mode=None, # 不生成标签\n",
    "        shuffle=False)  # 不随机打乱\n",
    "# 得到预测集数据\n",
    "bottleneck_features_test = model.predict_generator(generator, test_steps)\n",
    "print(bottleneck_features_test.shape)\n",
    "# 保存测试集bottleneck结果\n",
    "np.save(open('bottleneck_features_test.npy', 'wb'), bottleneck_features_test)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_data = np.load(open('bottleneck_features_train.npy','rb'))\n",
    "# the features were saved in order, so recreating the labels is easy\n",
    "labels = np.array([0] * 1000 + [1] * 1000)\n",
    "train_labels = np.array([])\n",
    "for _ in range(10):\n",
    "    train_labels=np.concatenate((train_labels,labels))\n",
    "\n",
    "test_data = np.load(open('bottleneck_features_test.npy','rb'))\n",
    "labels = np.array([0] * 500 + [1] * 500)\n",
    "test_labels = np.array([])\n",
    "for _ in range(10):\n",
    "    test_labels=np.concatenate((test_labels,labels))\n",
    "\n",
    "train_labels = np_utils.to_categorical(train_labels,num_classes=2)\n",
    "test_labels = np_utils.to_categorical(test_labels,num_classes=2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model = Sequential()\n",
    "model.add(Flatten(input_shape=train_data.shape[1:]))\n",
    "model.add(Dense(256, activation='relu'))\n",
    "model.add(Dropout(0.5))\n",
    "model.add(Dense(2, activation='softmax'))\n",
    "\n",
    "# 定义优化器\n",
    "adam = Adam(lr=1e-4)\n",
    "\n",
    "# 定义优化器，loss function，训练过程中计算准确率\n",
    "model.compile(optimizer=adam,loss='categorical_crossentropy',metrics=['accuracy'])\n",
    "\n",
    "model.fit(train_data, train_labels,\n",
    "          epochs=20, batch_size=batch_size,\n",
    "          validation_data=(test_data, test_labels))\n",
    "\n",
    "model.save_weights('bottleneck_fc_model.h5')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
